package main

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode/utf8"
)

type Age uint

var (
	sum475 = 0
	// 互斥锁
	//mutex sync.Mutex
	// 读写锁
	mutex sync.RWMutex
)

func main() {

	fmt.Println("Hello, 世界")
	var i int = 88
	fmt.Print(i)

	var s1 string = "Hello"
	var s2 string = "世界"
	fmt.Println("s1 is", s1, ",s2 is", s2)

	s1 += s2
	fmt.Println("s1 is", s1)

	// 变量的简短申明
	i2 := 10
	fmt.Println(i2)

	bf := false
	fmt.Println(bf)

	s3 := "Hello"
	fmt.Println(s3)

	// 变量i的指针（就是变量的内存地址）
	pi := &i
	fmt.Println(pi)

	// 常量
	const name = "caojing"
	fmt.Println(name)

	// iota 是一个常量生成器
	const (
		one = iota + 1
		two
		three
		four
	)
	fmt.Println(one, two, three, four)

	// int -> string
	i2s := strconv.Itoa(i)
	// string -> int
	s2i, err := strconv.Atoi(i2s)
	fmt.Println(i2s, s2i, err)

	var f64 float64 = 10.3456
	// int -> float64
	i2f := float64(i)
	// float64 -> int
	f2i := int(f64)
	fmt.Println(i2f, f2i)

	// 判断s1的前缀是否是H
	fmt.Println(strings.HasPrefix(s1, "H"))

	// 在s1中查找字符串o
	fmt.Println(strings.Index(s1, "o"))

	// 把s1全部转为大写
	fmt.Println(strings.ToUpper(s1))

	i4 := strings.Index("实现人生财富自由", "自由")
	fmt.Println(i4)

	// if else
	// i5 := 10只能在if else里面使用
	if i5 := 10; i5 > 8 {
		fmt.Println("大于8")
	} else if i5 > 4 && i <= 8 {
		fmt.Println("大于4小于等于8")
	} else {
		fmt.Println("小于等于4")
	}

	// switch
	switch i6 := 6; {
	case i6 > 10:
		fmt.Println("大于10")
	case i6 > 4 && i6 <= 8:
		fmt.Println("大于4小于等于8")
	default:
		fmt.Println("小于等于4")
	}

	switch j := 1; j {
	case 1:
		fallthrough
	case 2:
		fmt.Println("1")
	default:
		fmt.Println("没有匹配")
	}

	switch 2 > 1 {
	case true:
		fmt.Println("2 > 1")
	case false:
		fmt.Println("2 <= 1")
	}

	// for循环
	sum := 0
	for i := 0; i < 100; i++ {
		sum += i
	}
	fmt.Println(sum)

	i7 := 1
	for i7 < 100 {
		sum += i7
		i7++
	}
	fmt.Println("和是", sum)

	// break
	for {
		sum += i7
		i7++
		if i7 > 200 {
			break
		}
	}
	fmt.Println("和是", sum)

	// continue
	sum2 := 0
	for i := 0; i <= 100; i++ {
		if i%2 != 0 {
			continue
		}
		sum2 += i
	}
	fmt.Println(sum2)

	// 数组
	array := [5]string{"a", "b", "c", "e", "f"}
	// 等价于上面的👆写法
	//array := [...]string{"a", "b", "c", "e", "f"}
	fmt.Println(array[2])

	// 遍历数组 for range
	for i, v := range array {
		fmt.Println("数组索引:%d,对应值:%s,\n", i, v)
	}
	// _表示丢弃索引
	for _, v := range array {
		fmt.Println("对应值:%s\n", v)
	}
	// 基于数组生成切片，包含索引start,但是不包含索引end
	// array[:4]等价于array[0:4]
	// array[1:]等价于array[1:5]
	// array[:]等价于array[0:5]
	slice := array[2:5]
	fmt.Println(slice)

	slice1 := make([]string, 4)
	fmt.Println(slice1)

	slice2 := make([]string, 4, 8)
	fmt.Println(slice2)

	// 切片声明
	slice3 := []string{"a", "b", "c", "d", "e", "f"}
	// 打印切片长度、容量
	fmt.Println(len(slice3), cap(slice3))

	// 追加一个元素
	slice4 := append(slice3, "g")
	fmt.Println(slice4)
	// 追加多个元素
	slice5 := append(slice3, "g", "10")
	fmt.Println(slice5)
	// 追加切片
	slice6 := append(slice4, slice5...)
	fmt.Println(slice6)

	// 通过make函数创建map
	nameAgeMap := make(map[string]int)
	nameAgeMap["caojing"] = 996
	fmt.Println(nameAgeMap)

	genderMap := map[string]string{"caojing": "男"}
	fmt.Println(genderMap)
	// 空map
	genderMap2 := map[string]string{}
	fmt.Println(genderMap2)

	// 修改map的值
	genderMap["caojing"] = "大男人"
	// 获取map的值
	gender := genderMap["caojing"]
	fmt.Println(gender)

	gender2, ok := genderMap["女朋友"]
	if ok {
		fmt.Println(gender2)
	} else {
		fmt.Println("没有女朋友")
	}
	genderMap["女朋友"] = "女"
	fmt.Println(genderMap)

	delete(genderMap, "女朋友")
	fmt.Println(genderMap)

	genderMap["张一鸣"] = "男"
	// 遍历key、value
	for k, v := range genderMap {
		fmt.Println("key is", k, "value is", v)
	}
	fmt.Println(len(genderMap))

	// 遍历key
	for k := range genderMap {
		fmt.Println("key is", k)
	}

	s8 := "caojing英俊潇洒风流倜傥"
	bs := []byte(s8)
	fmt.Println(bs)
	// 获取对应索引的字段值
	fmt.Println(s8[0], s8[1])

	// utf8编码下，一个汉字对应三个字节，len其实表示字节长度
	fmt.Println(len(s8))
	// 字符长度
	fmt.Println(utf8.RuneCountInString(s8))
	// 遍历字符串字符
	for i, s := range s8 {
		fmt.Println("索引:", i, "字符:", s)
	}

	// 二维数组
	aa := [3][3]int{}
	aa[0][0] = 1
	aa[0][1] = 2
	aa[0][2] = 3
	aa[1][0] = 4
	aa[1][1] = 5
	aa[1][2] = 6
	aa[2][0] = 7
	aa[2][1] = 8
	aa[2][2] = 9
	fmt.Println(aa)

	// 函数
	result := func1(4, 5)
	fmt.Println(result)

	// 多值返回函数
	result, error := fun2(1, 2)
	if error != nil {
		fmt.Println(error)
	} else {
		fmt.Println(result)
	}

	// 如果有的函数的返回值不需要，可以使用下划线 _ 丢弃它
	result, _ = fun2(1, 2)
	fmt.Println(result)

	// 返回值命名的函数，不太常用
	result, error = fun3(1, 2)
	if error != nil {
		fmt.Println(error)
	} else {
		fmt.Println(result)
	}

	// 可变参数函数
	fmt.Println(fun4(1, 2))
	fmt.Println(fun4(1, 2, 3))
	fmt.Println(fun4(1, 2, 3, 4))

	// 函数作用域要公有就首字母要大写，其他包就可以调用
	// 函数名称小写就是私有函数，只有在同一个包中才可以被调用

	// 匿名函数，下面f是函数类型的变量不是函数
	f := func(a, b int) int {
		return a + b
	}
	fmt.Println(f(6, 7))

	// 无参匿名函数
	g := func() {
		fmt.Println(666666)
	}
	// 执行匿名函数
	g()

	// 匿名函数定义及执行
	func() {
		fmt.Println(888888)
	}()

	// 闭包：在函数中再定义函数（函数嵌套），定义的这个匿名函数，也可以称为内部函数
	// 在函数内定义的内部函数，可以使用外部函数的变量等，这种方式也称为闭包
	c1 := closure()
	fmt.Println(c1())
	fmt.Println(c1())
	fmt.Println(c1())

	// 类型以及方法
	age := Age(5)
	age.pp()
	age.modify()
	age.pp()

	// 方法赋值给变量，方法表达式
	sm := Age.pp
	sm(age)

	// 结构体初始化
	p := person{age: 18, name: "caojing", addr: address{province: "陕西", city: "西安"}}
	fmt.Println(p)

	// 获取内部结构体属性
	fmt.Println(p.addr.city)

	printString(p.addr)

	p1 := NewPerson("曹靖666666")
	printString(p1)

	var s fmt.Stringer
	s = p1
	// 类型断言，判断s是否为person指针
	p2 := s.(*person)
	fmt.Println(p2)

	// 这种类型断言不会抛异常
	a, ok := s.(address)
	if ok {
		fmt.Println(a)
	} else {
		fmt.Println("s不是一个address")
	}

	// 返回错误信息
	i, err = strconv.Atoi("a")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(i)
	}

	sum348, err348 := add(-1, 2)
	if err348 != nil {
		fmt.Println(err348)
	} else {
		fmt.Println(sum348)
	}

	sum355, err := add(-1, 2)
	// error断言（不推荐）
	if cm, ok := err.(*commonError); ok {
		fmt.Printf("错误代码为:", cm.errorCode, "错误信息为:", cm.errorMsg)
	} else {
		fmt.Println(sum355)
	}

	var cm *commonError
	// 推荐errors.As做error断言，不知道一个 error 是否被嵌套，又嵌套了几层
	if errors.As(err, &cm) {
		fmt.Printf("错误代码为:", cm.errorCode, "错误信息为:", cm.errorMsg)
	} else {
		fmt.Println(sum355)
	}

	// 包装error（不推荐）
	mewErr := MyError{err, "数据上传问题"}
	fmt.Println(mewErr.msg)

	e := errors.New("原始错误e")
	// 错误嵌套
	w := fmt.Errorf("包装了一个错误:%w", e)
	fmt.Println(w)

	// errors.Unwrap用于获取被嵌套的error
	fmt.Println(errors.Unwrap(w))

	// 判断两个 error 是否是同一个
	// 如果 err 和 target 是同一个，那么返回 true
	// 如果 err 是一个 wrapping error，target 也包含在这个嵌套 error 链中的话，也返回 true。
	// 可以简单地概括为，两个 error 相等或 err 包含 target 的情况下返回 true，其余返回 false。
	fmt.Println(errors.Is(w, e))

	// defer 关键字用于修饰一个函数或者方法，使得该函数或者方法在返回前才会执行，也就说被延迟，但又可以保证一定会执行。
	// 通过内置的recover函数恢复panic异常
	// Recover 捕获Panic异常
	defer func() {
		if p := recover(); p != nil {
			fmt.Println(p)
		}
	}()

	// panic 异常是一种非常严重的情况，会让程序中断运行，使程序崩溃，所以如果是不影响程序运行的错误，不要使用 panic，使用普通错误 error 即可。
	//connectMySql("", "root", "123456")

	// 在一个方法或者函数中，可以有多个 defer 语句；多个 defer 语句的执行顺序依照后进先出的原则。
	moreDefer()

	// 协程 goroutine
	go fmt.Println("曹靖666")
	fmt.Println("曹靖财富自由")
	// 等待1秒
	time.Sleep(time.Second)

	// 通道，并发安全。无缓冲的channel
	ch := make(chan string)
	go func() {
		fmt.Println("协程飞起来")
		ch <- "goroutine完成"
	}()

	fmt.Println("我是main goroutine")

	v := <-ch
	fmt.Println("接收到通道中的值为:", v)

	// 有缓冲的channel
	cacheCh := make(chan int, 5)
	cacheCh <- 2
	cacheCh <- 3
	fmt.Println("cacheCh容量为:", cap(cacheCh), ",元素个数为:", len(cacheCh))

	// 单向channel
	// 单向发送channel
	//onlySend := make(chan<- int)
	// 单向接收channel
	//onlyReceive := make(<-chan int)

	// 声明3个存放结果的channel
	firstCh := make(chan string)
	secondCh := make(chan string)
	threeCh := make(chan string)

	// 同时开启3个goroutine下载
	go func() {
		firstCh <- downloadFile("firstCh")
	}()

	go func() {
		secondCh <- downloadFile("secondCh")
	}()

	go func() {
		threeCh <- downloadFile("threeCh")
	}()

	// 开始select多路复用，哪个channel能获取到值就说明哪个最先下载好，就用那个
	select {
	case firstPath := <-firstCh:
		fmt.Println(firstPath)
	case firstPath := <-secondCh:
		fmt.Println(firstPath)
	case firstPath := <-threeCh:
		fmt.Println(firstPath)
	}

	run()

	doOnce()

	race()

	var smap sync.Map
	// Store：存储一对 key-value 值。
	smap.Store(1, "a")
	// Load：根据 key 获取对应的 value 值，并且可以判断 key 是否存在。
	if v, ok := smap.Load(1); ok {
		fmt.Println(v)
	}
	// LoadOrStore：如果 key 对应的 value 存在，则返回该 value；如果不存在，存储相应的 value。
	if vv, ok := smap.LoadOrStore(1, "c"); ok {
		fmt.Println(vv)
	}
	if vv, ok := smap.LoadOrStore(2, "ok"); !ok {
		fmt.Println(vv)
	}
	// 遍历该map，参数是个函数，该函数参的两个参数是遍历获得的key和value，返回一个bool值，当返回false时，遍历立刻结束。
	smap.Range(func(k, v interface{}) bool {
		fmt.Println(k, ":", v)
		return true
	})
	// Delete：删除一个 key-value 键值对。
	smap.Delete(2)
	smap.Range(func(k, v interface{}) bool {
		fmt.Println(k, ":", v)
		return true
	})

	var wg sync.WaitGroup
	wg.Add(4)
	//stopCh := make(chan bool) // 用来停止监控狗
	ctx, stop := context.WithCancel(context.Background()) // 用来停止监控狗
	go func() {
		defer wg.Done()
		//watchDog(stopCh, "【监控狗1】")
		watchDog(ctx, "【监控狗1】")
	}()

	go func() {
		defer wg.Done()
		watchDog(ctx, "【监控狗2】")
	}()

	go func() {
		defer wg.Done()
		watchDog(ctx, "【监控狗3】")
	}()

	valCtx := context.WithValue(ctx, "userId", 2)
	go func() {
		defer wg.Done()
		getUser(valCtx)
	}()
	time.Sleep(5 * time.Second) // 先让监控狗监控5秒
	stop()                      // 发停止指令
	wg.Wait()
}

func func1(a, b int) int {
	return a + b
}

func fun2(a, b int) (int, error) {
	if a < 0 || b < 0 {
		return 0, errors.New("a或者b不能是负数")
	}
	return a + b, nil
}

func fun3(a, b int) (sum int, err error) {
	if a < 0 || b < 0 {
		return 0, errors.New("a或者b不能是负数")
	}
	sum = a + b
	err = nil
	return
}

func fun4(a ...int) int {
	total := 0
	for _, i := range a {
		total += i
	}
	return total
}

func closure() func() int {
	i := 0
	return func() int {
		i++
		return i
	}
}

// 值类型接收者
func (age Age) pp() {
	fmt.Println("the age is", age)
}

// 前面带*表示相应的指针类型接收者
func (age *Age) modify() {
	*age = Age(30)
}

// 结构体
type person struct {
	name string
	age  uint
	addr address
}

type address struct {
	province string
	city     string
}

// 接口
//type Stringer interface {
//	String() string
//}

// 以值类型实现接口的时候【类型本身】和【该类型的指针类型】都实现了该接口
func (p person) String() string {
	return fmt.Sprintf("the name is %s,age is %d", p.name, p.age)
}

func (addr address) String() string {
	return fmt.Sprintf("the addr is %s%s", addr.province, addr.city)
}

func printString(s fmt.Stringer) {
	fmt.Println(s.String())
}

// 工厂函数
func NewPerson(name string) *person {
	return &person{name: name}
}

// 工厂函数，返回一个error接口，其实具体实现是*errorString
func New(text string) error {
	return &errorString{text}
}

// 结构体，内部一个字段s,存储错误信息
type errorString struct {
	s string
}

// 用于实现error接口
func (e *errorString) Error() string {
	return e.s
}

// 有两个方法的接口
type WalkRun interface {
	Walk()
	Run()
}

func (p *person) Walk() {
	fmt.Printf("%s能走\n", p.name)
}

func (p *person) Run() {
	fmt.Printf("%s能跑\n", p.name)
}

func add(a, b int) (int, error) {
	if a < 0 || b < 0 {
		//return 0, errors.New("a或者b不能为负数")
		return 0, &commonError{errorCode: 500, errorMsg: "a或者b不能为负数"} // 返回错误码和错误信息
	} else {
		return a + b, nil
	}
}

type commonError struct {
	errorCode int    // 错误码
	errorMsg  string // 错误信息
}

func (ce *commonError) Error() string {
	return ce.errorMsg
}

type MyError struct {
	err error
	msg string
}

func (e *MyError) Error() string {
	return e.err.Error() + e.msg
}

func connectMySql(ip, username, password string) {
	if ip == "" {
		panic("ip不能为空")
	}
	// 省略其他代码
}

func moreDefer() {
	defer fmt.Println("First defer")
	defer fmt.Println("Second defer")
	defer fmt.Println("Three defer")
	fmt.Println("函数自身代码")
}

// 发送通道为入参的函数
func counter(out chan<- int) {
	// 函数内容使用变量out，只能进行发送操作
}

func downloadFile(chanName string) string {
	// 模拟下载文件，可以自己随机time.Sleep点时间sh试试
	time.Sleep(time.Second)
	return chanName + ":filePath"
}

func sumAdd(i int) {
	mutex.Lock()
	defer mutex.Unlock()
	sum475 += i
}

// 增加一个读取sum的函数，便于演示并发
func readSum() int {
	//mutex.Lock()
	// 只获取读锁
	mutex.RLock()
	//defer mutex.Unlock()
	defer mutex.RUnlock()
	b := sum475
	return b
}

func run() {
	var wg sync.WaitGroup
	// 因为要监控110个协程，所以设置计数器为110
	wg.Add(110)

	// 开启100个线程让sum475 + 10
	for i := 0; i < 100; i++ {
		go func() {
			// 计数器值减一
			defer wg.Done()
			sumAdd(10)
		}()
	}
	for i := 0; i < 10; i++ {
		go func() {
			// 计数器值减一
			defer wg.Done()
			fmt.Println("和为：", readSum())
		}()
	}
	// 防止提前退出
	//time.Sleep(2 * time.Second)
	// 一直等待，只要计数器值为0
	wg.Wait()
	fmt.Println("和为：", sum475)
}

func doOnce() {
	// sync.Once保证代码只执行一次
	// sync.Once 适用于创建某个对象的单例、只加载一次的资源等只执行一次的场景。
	var once sync.Once
	oneBody := func() {
		fmt.Println("Only once")
	}
	// 用于等待协程执行完毕
	done := make(chan bool)
	// 启动10个协程执行once.Do(onceBody)
	for i := 0; i < 10; i++ {
		go func() {
			// 把要执行的函数（方法）作为参数传给once.Do方法即可
			once.Do(oneBody)
			done <- true
		}()
	}
	for i := 0; i < 10; i++ {
		<-done
	}
}

func race() {
	// 通过 sync.NewCond 函数生成一个 *sync.Cond，用于阻塞和唤醒协程
	cond := sync.NewCond(&sync.Mutex{})
	var wg sync.WaitGroup
	wg.Add(11)
	for i := 0; i < 10; i++ {
		go func(num int) {
			defer wg.Done()
			fmt.Println(num, "号已经就位")
			cond.L.Lock()
			// 阻塞当前协程，直到被其他协程调用 Broadcast 或者 Signal 方法唤醒，使用的时候需要加锁，使用 sync.Cond 中的锁即可，也就是 L 字段。
			cond.Wait() // 等待发令枪响
			fmt.Println(num, "号开始跑......")
			cond.L.Unlock()
		}(i)
	}
	// 等待所有goroutine都进入wait状态
	time.Sleep(2 * time.Second)
	go func() {
		defer wg.Done()
		fmt.Println("裁判已经就位，准备发令枪")
		fmt.Println("比赛开始，大家准备跑")
		// sync.Cond.Broadcast() 唤醒所有等待的协程
		cond.Broadcast() // 发令枪响
	}()
	// 防止函数提前返回
	wg.Wait()
}

// stopCh用于接收停止指令
//func watchDog(stopCh chan bool, name string) {
//	// 开启for select循环，一直后台监控
//	for {
//		select {
//		case <-stopCh:
//			fmt.Println(name, "停止指令已收到，马上停止")
//			return
//		default:
//			fmt.Println(name, "正在监控......")
//		}
//		time.Sleep(1 * time.Second)
//	}
//}

// stopCh用于接收停止指令
func watchDog(ctx context.Context, name string) {
	// 开启for select循环，一直后台监控
	for {
		select {
		case <-ctx.Done():
			fmt.Println(name, "停止指令已收到，马上停止")
			return
		default:
			fmt.Println(name, "正在监控......")
		}
		time.Sleep(1 * time.Second)
	}
}

func getUser(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("【获取用户】", "协程退出")
			return
		default:
			userId := ctx.Value("userId")
			fmt.Println("【获取用户】", "用户ID为：", userId)
			time.Sleep(1 * time.Second)
		}
	}
}
